#include <reserver.h>
#include <user.h>
#include <defs.h>
#include <messages.h>
#include <io.h>
#include <utils.h>
#include <printer.h>
#include <track.h>
#include <curses.h>

// Adapted from line algorithm at http://www.edepot.com/lined.html
void DrawLine(int x, int y, int x2, int y2) {
  int yLonger=FALSE;
  int incrementVal, endVal;
  int shortLen=y2-y;
  int longLen=x2-x;
  if (Abs(shortLen)>Abs(longLen)) {
    int swap=shortLen;
    shortLen=longLen;
    longLen=swap;
    yLonger=TRUE;
  }
  endVal=longLen;
  if (longLen<0) {
    incrementVal=-1;
    longLen=-longLen;
  } else incrementVal=1;
  int decInc;
  if (longLen==0) decInc=0;
  else decInc = (shortLen << 16) / longLen;
  int j=0;
  if (yLonger) {
    int i;
    for (i=0;i!=endVal;i+=incrementVal) {
      PrintAt(1+(x+(j>>16)), 6+(y+i), " ");
      j+=decInc;
    }
  } else {
    int i;
    for (i=0;i!=endVal;i+=incrementVal) {
      PrintAt(1+(x+i), 6+(y+(j>>16)), " ");
      j+=decInc;
    }
  }
}

void UpdateTrackDisplay(int bg, int v1, int v2) {
  // figure out (x, y) positions of v1, v2
  track_node n1, n2;
  GetNode(v1, &n1);
  GetNode(v2, &n2);
  // TODO: this next part might need to be made atomic somehow
  // set appropriate screen colour
  SetScreenColor(FG_BASE+WHITE_OFFSET, bg);
  // print line between two locations
  int r1 = n1.y/22, r2 = n2.y/22;
  int c1 = n1.x/16, c2 = n2.x/16;
  DrawLine(c1, r1, c2, r2);
  // reset screen colour
  SetScreenColor(FG_BASE+WHITE_OFFSET, BG_BASE+BLACK_OFFSET);
}

void ReservationServer() {
  int track[72][72];
  int i, j;
  for (i = 0; i < 72; i++)
    for (j = 0; j < 72; j++)
      track[i][j] = FREE;
  
  reserve_req msg;
  reserve_reply reply;
  int sender_tid;
  resource_event event;
  
  // different players
  int red_player = UNDEFINED;
  int blue_player = UNDEFINED;
  
  char name1[5];
  char name2[5];

  // notify parent
  int parent_tid;
  Receive(&parent_tid, NULL, 0);
  Reply(parent_tid, NULL, 0);

  RegisterAs("reservation-server");
  
  for (;;) {    
    Receive( &sender_tid, (char*)&msg, sizeof(reserve_req));
    // update players if necessary
    if (red_player == UNDEFINED)
      red_player = sender_tid;
    else if (red_player != sender_tid && blue_player == UNDEFINED)
      blue_player = sender_tid;

    // deal with message
    switch (msg.type) {
      case RESERVE_TRACK_REQUEST:
        if (track[msg.v1][msg.v2] == FREE) {
          InternalToExternal(msg.v1, name1);
          InternalToExternal(msg.v2, name2);
          PrintAt(1, 24, "Train reserving %s[%d] - %s[%d]\r\n", name1, msg.v1, name2, msg.v2);
          track[msg.v1][msg.v2] = sender_tid;
          track[msg.v2][msg.v1] = sender_tid;
          reply.result = TRUE;
          // for Tron game, update the track display
          int bg = (sender_tid == red_player) ? RED_OFFSET : BLUE_OFFSET;
          bg = BG_BASE+bg;
          UpdateTrackDisplay(bg, msg.v1, msg.v2);
        }
        else {
          reply.result = FALSE;
        }
        break;
      case FREE_TRACK_REQUEST:
        if (track[msg.v1][msg.v2] == sender_tid) {        
          InternalToExternal(msg.v1, name1);
          InternalToExternal(msg.v2, name2);
          PrintAt(1, 24, "Train freeing %s[%d] - %s[%d]\r\n", name1, msg.v1, name2, msg.v2);
          track[msg.v1][msg.v2] = FREE;          
          track[msg.v2][msg.v1] = FREE;
          reply.result = TRUE;          
          
          event.type = TRACK;
          event.arg0 = msg.v1;
          event.arg1 = msg.v2;
          NotifyEvent(EVENT_FREE_RESOURCE, (char*)&event, sizeof(resource_event));
        }
        else {
          Printf("Task ID: %d\r\n", sender_tid);
          Panic("ReservationServer", "Invalid attempt to free a piece of track.");
        }
        break;
      case RESERVE_SWITCH_REQUEST:
        if (track[msg.v1][msg.v1] == FREE) {          
          InternalToExternal(msg.v1, name1);
          PrintAt(1, 24, "Train reserving switch %s[%d]\r\n", name1, msg.v1);
          track[msg.v1][msg.v1] = sender_tid;
          reply.result = TRUE;
        }
        else {
          reply.result = FALSE;
        }
        break;
      case FREE_SWITCH_REQUEST:
        if (track[msg.v1][msg.v1] == sender_tid) {
          InternalToExternal(msg.v1, name1);
          PrintAt(1, 24, "Train freeing switch %s[%d]\r\n", name1, msg.v1);
          track[msg.v1][msg.v1] = FREE;
          reply.result = TRUE;
          
          event.type = SWITCH;
          event.arg0 = msg.v1;
          NotifyEvent(EVENT_FREE_RESOURCE, (char*)&event, sizeof(resource_event));
        }
        else {
          Printf("Task ID: %d\r\n", sender_tid);
          Panic("ReservationServer", "Invalid attempt to free a switch.");
        }
        break;     
      default:
        Panic("ReservationServer", "Invalid request.");
    }
    
    Reply(sender_tid, (char*)&reply, sizeof(reserve_reply));
  }
  Panic("ReservationServer", "Chuck Norris!");
}

int ReserveTrack(int v1, int v2) {
  reserve_req msg;
  reserve_reply reply;
  msg.type = RESERVE_TRACK_REQUEST;
  msg.v1 = v1;
  msg.v2 = v2;
  int reserver_tid = WhoIs("reservation-server");
  
  Send(reserver_tid, (char*)&msg, sizeof(reserve_req), (char*)&reply, sizeof(reserve_reply));
  return reply.result;
}

int ReserveSwitch(int id) {
  reserve_req msg;
  reserve_reply reply;
  msg.type = RESERVE_SWITCH_REQUEST;
  msg.v1 = id;
  int reserver_tid = WhoIs("reservation-server");
  
  Send(reserver_tid, (char*)&msg, sizeof(reserve_req), (char*)&reply, sizeof(reserve_reply));
  return reply.result;
}

void FreeTrack(int v1, int v2) {
  reserve_req msg;
  reserve_reply reply;
  msg.type = FREE_TRACK_REQUEST;
  msg.v1 = v1;
  msg.v2 = v2;
  int reserver_tid = WhoIs("reservation-server");
  
  Send(reserver_tid, (char*)&msg, sizeof(reserve_req), (char*)&reply, sizeof(reserve_reply));
}

void FreeSwitch(int id) {
  reserve_req msg;
  reserve_reply reply;
  msg.type = FREE_SWITCH_REQUEST;
  msg.v1 = id;
  int reserver_tid = WhoIs("reservation-server");
  
  Send(reserver_tid, (char*)&msg, sizeof(reserve_req), (char*)&reply, sizeof(reserve_reply));
}
